package testrank;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import testrank.affinity.AffinityPreprocessor;
import testrank.affinity.WordPair;
import testrank.predictors.AffinityPredictor;
import testrank.predictors.AffinityPropagationPredictor;
import testrank.predictors.AveragePredictor;
import testrank.predictors.CallCountPredictor;
import testrank.predictors.ExecutionCountPredictor;
import testrank.predictors.InverseCoveragePredictor;
import testrank.predictors.StackDepthExecutionCountPredictor;
import testrank.predictors.ValuePropagationPredictor;
/**
 * Holds the predictors, source elements locators and affinity preprocessor
 *  
 * @author Hagai Cibulski
 */
public class TestManager {
	public static final String FILE = "e:/dev/workspace/testrank.ser";
	public static final String AFFINITIES_FILE = "e:/dev/workspace/affinities.ser";
	// TODO configure file
	// if(args.length > 0) {
	// filename = args[0];
	// }

	private static InverseCoveragePredictor coveragePredictor = new InverseCoveragePredictor();
	
	private static ExecutionCountPredictor countPredictor = new ExecutionCountPredictor();
	
	private static AffinityPredictor affinityPredictor = new AffinityPredictor(countPredictor);
	
	private static StackDepthExecutionCountPredictor sdCountPredictor = new StackDepthExecutionCountPredictor();

	private static CallCountPredictor callCountPredictor = new CallCountPredictor();
	
	private static AffinityPropagationPredictor affinityPropagationPredictor = new AffinityPropagationPredictor(affinityPredictor, callCountPredictor);
	
	private static ValuePropagationPredictor valuePropagationPredictor = new ValuePropagationPredictor();
	
	private static Predictor averagePredictor = new AveragePredictor();
	
	private static SourceElementLocator sourceElementLocator = new SourceElementLocator();
	
	private static SourceElementLocator testLocator = new SourceElementLocator();

	private static AffinityPreprocessor processor = new AffinityPreprocessor();

	private static Map<WordPair, Double> wordAffinities = new HashMap<WordPair, Double>();
	
	public /*final*/ static	Predictor[]	
	predictors = 
		new Predictor[] {
			countPredictor,
			callCountPredictor,
			sdCountPredictor,
			valuePropagationPredictor,
			coveragePredictor,
			
			affinityPredictor,
			affinityPropagationPredictor,
	};
	

	public static ExecutionCountPredictor predictor() {
		return countPredictor;
	}
	
	public static AffinityPredictor affinityPredictor() {
		return affinityPredictor;
	}
	
	public static StackDepthExecutionCountPredictor sdCountPredictor() {
		return sdCountPredictor;
	}
	
	public static CallCountPredictor callCountPredictor() {
		return callCountPredictor;
	}
	
	public static AffinityPropagationPredictor affinityPropagationPredictor() {
		return affinityPropagationPredictor;
	}

	public static ValuePropagationPredictor valuePropagationPredictor() {
		return valuePropagationPredictor;
	}
	
	public static InverseCoveragePredictor coveragePredictor() {
		return coveragePredictor;
	}

	public static SourceElementLocator locator() {
		return sourceElementLocator;
	}
	
	public static SourceElementLocator testLocator() {
		return testLocator;
	}

	public static AffinityPreprocessor processor() {
		return processor;
	}
	
	public static Map<WordPair, Double> wordAffinities() {
		return wordAffinities;
	}

	public static void setWordAffinities(Map<WordPair, Double> wordAffinities) {
		TestManager.wordAffinities = wordAffinities;
	}

	public static void setProcessor(AffinityPreprocessor processor) {
		TestManager.processor = processor;
	}
	
	public static void save() {
		savePredictors(FILE);
	}	
	
	public static void load() {
		load(FILE);
	}	

	public static void saveAffinities() {
		save(AFFINITIES_FILE, wordAffinities);
	}	
	
	public static void loadAffinities() {
		if (new File(AFFINITIES_FILE).exists()) {
			load(AFFINITIES_FILE);
		}
	}	

	public static void save(String file, Object... objects) {
		FileOutputStream fos = null;
		ObjectOutputStream out = null;
		try {
			fos = new FileOutputStream(file);
			out = new ObjectOutputStream(fos);
			
			for (Object object : objects) {
				out.writeObject(object);				
			}
			
			out.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
	public static void savePredictors(String file) {
		FileOutputStream fos = null;
		ObjectOutputStream out = null;
		try {
			fos = new FileOutputStream(file);
			out = new ObjectOutputStream(fos);
			
			for (Predictor predictor : predictors) {
				if ( ! (predictor instanceof AveragePredictor)) {
					out.writeObject(predictor);
				}
			}
			out.writeObject(sourceElementLocator);
			out.writeObject(testLocator);
			
			out.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
	public static void load(String file) {
		FileInputStream fis = null;
		ObjectInputStream in = null;
		try {
			fis = new FileInputStream(file);
			in = new ObjectInputStream(fis);
			
			if (file.equals(FILE)) {// TODO hack hack hack...
				
				for (int i = 0; i < predictors.length; i++) { 
					predictors[i] = (Predictor)in.readObject();
				}
				
				// TODO ugly...
//				int i = 0;
//				countPredictor = (ExecutionCountPredictor) predictors[i++];
//				affinityPredictor = (AffinityPredictor) predictors[i++]; 
//				sdCountPredictor = (StackDepthExecutionCountPredictor) predictors[i++];
//				callCountPredictor = (CallCountPredictor) predictors[i++];
//				affinityPropagationPredictor = (AffinityPropagationPredictor) predictors[i++];
//				valuePropagationPredictor = (ValuePropagationPredictor) predictors[i++];
				
				for (Predictor p : predictors) {
					if (p instanceof ExecutionCountPredictor)
						countPredictor = (ExecutionCountPredictor) p;
					else if (p instanceof CallCountPredictor)
						callCountPredictor = (CallCountPredictor)p;
					else if (p instanceof StackDepthExecutionCountPredictor)
						sdCountPredictor = (StackDepthExecutionCountPredictor)p;
					else if (p instanceof ValuePropagationPredictor)
						valuePropagationPredictor = (ValuePropagationPredictor)p;
					else if (p instanceof InverseCoveragePredictor)
						coveragePredictor = (InverseCoveragePredictor)p;				
					else if (p instanceof AffinityPredictor)
						affinityPredictor = (AffinityPredictor)p;
					else if (p instanceof AffinityPropagationPredictor)
						affinityPropagationPredictor = (AffinityPropagationPredictor)p;
				}
				
				
				averagePredictor = new AveragePredictor();//TODO override whatever was previously saved
				{
					List<Predictor> cps = new ArrayList<Predictor>(Arrays.asList(predictors));
					cps.add(averagePredictor);
					predictors = (Predictor[]) cps.toArray(new Predictor[0]);
				}

				sourceElementLocator = (SourceElementLocator) in.readObject();
				testLocator = (SourceElementLocator) in.readObject();
			}
			else if (file.equals(AFFINITIES_FILE)) {
				loadAffinities(in);
			}
				
			in.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	private static void loadAffinities(ObjectInputStream in)
			throws IOException, ClassNotFoundException {
		wordAffinities = (Map<WordPair, Double>) in.readObject();
	}
}
